/*!
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

/**
 * Constant enumeration of legal values returned by HTMLElement.tagName property.
 *
 * Note: `TagName` are uppercase for comparison '===' with the 'tagName' property of Element.
 *       Unfortunately, this is the opposite of the casing used for document.createElement().
 */
export const enum TagName {
    a = "A",
    abbr = "ABBR",
    address = "ADDRESS",
    area = "AREA",
    article = "ARTICLE",
    aside = "ASIDE",
    audio = "AUDIO",
    b = "B",
    base = "BASE",
    bdi = "BDI",
    bdo = "BDO",
    blockquote = "BLOCKQUOTE",
    body = "BODY",
    br = "BR",
    button = "BUTTON",
    canvas = "CANVAS",
    caption = "CAPTION",
    cite = "CITE",
    code = "CODE",
    col = "COL",
    colgroup = "COLGROUP",
    data = "DATA",
    datalist = "DATALIST",
    dd = "DD",
    del = "DEL",
    details = "DETAILS",
    dfn = "DFN",
    dialog = "DIALOG",
    div = "DIV",
    dl = "DL",
    dt = "DT",
    em = "EM",
    embed = "EMBED",
    fieldset = "FIELDSET",
    figcaption = "FIGCAPTION",
    figure = "FIGURE",
    footer = "FOOTER",
    form = "FORM",
    h1 = "H1",
    h2 = "H2",
    h3 = "H3",
    h4 = "H4",
    h5 = "H5",
    h6 = "H6",
    head = "HEAD",
    header = "HEADER",
    hr = "HR",
    html = "HTML",
    i = "I",
    iframe = "IFRAME",
    img = "IMG",
    input = "INPUT",
    ins = "INS",
    kbd = "KBD",
    keygen = "KEYGEN",
    label = "LABEL",
    legend = "LEGEND",
    li = "LI",
    link = "LINK",
    main = "MAIN",
    map = "MAP",
    mark = "MARK",
    menu = "MENU",
    menuitem = "MENUITEM",
    meta = "META",
    meter = "METER",
    nav = "NAV",
    noscript = "NOSCRIPT",
    object = "OBJECT",
    ol = "OL",
    optgroup = "OPTGROUP",
    option = "OPTION",
    output = "OUTPUT",
    p = "P",
    param = "PARAM",
    picture = "PICTURE",
    pre = "PRE",
    progress = "PROGRESS",
    q = "Q",
    rb = "RB",
    rbc = "RBC",
    rp = "RP",
    rt = "RT",
    rtc = "RTC",
    ruby = "RUBY",
    s = "S",
    samp = "SAMP",
    script = "SCRIPT",
    section = "SECTION",
    select = "SELECT",
    slot = "SLOT",
    small = "SMALL",
    source = "SOURCE",
    span = "SPAN",
    strong = "STRONG",
    style = "STYLE",
    sub = "SUB",
    summary = "SUMMARY",
    sup = "SUP",
    table = "TABLE",
    tbody = "TBODY",
    td = "TD",
    template = "TEMPLATE",
    textarea = "TEXTAREA",
    tfoot = "TFOOT",
    th = "TH",
    thead = "THEAD",
    time = "TIME",
    title = "TITLE",
    tr = "TR",
    track = "TRACK",
    u = "U",
    ul = "UL",
    var = "VAR",
    video = "VIDEO",
    wbr = "WBR",
}

// Note: Similar to the `HTMLElementTagNameMap` defined in lib.dom.d.ts, except that keys are uppercase.
interface TagMap {
    [TagName.a]: HTMLAnchorElement,
    [TagName.abbr]: HTMLElement, // HTMLAbbrElement,
    [TagName.address]: HTMLElement, // HTMLAddressElement,
    [TagName.area]: HTMLAreaElement,
    [TagName.article]: HTMLElement, // HTMLArticleElement,
    [TagName.aside]: HTMLElement, // HTMLAsideElement,
    [TagName.audio]: HTMLAudioElement,
    [TagName.b]: HTMLElement, // HTMLBElement,
    [TagName.base]: HTMLBaseElement,
    [TagName.bdi]: HTMLElement, // HTMLBdiElement,
    [TagName.bdo]: HTMLElement, // HTMLBdoElement,
    [TagName.blockquote]: HTMLQuoteElement, // HTMLBlockquoteElement,
    [TagName.body]: HTMLBodyElement,
    [TagName.br]: HTMLBRElement,
    [TagName.button]: HTMLButtonElement,
    [TagName.canvas]: HTMLCanvasElement,
    [TagName.caption]: HTMLTableCaptionElement,
    [TagName.cite]: HTMLElement, // HTMLCiteElement,
    [TagName.code]: HTMLElement, // HTMLCodeElement,
    [TagName.col]: HTMLTableColElement,
    [TagName.colgroup]: HTMLTableColElement, // HTMLColGroupElement,
    [TagName.data]: HTMLDataElement,
    [TagName.datalist]: HTMLDataListElement,
    [TagName.dd]: HTMLElement, // HTMLDdElement,
    [TagName.del]: HTMLModElement, // HTMLDelElement,
    [TagName.details]: HTMLDetailsElement,
    [TagName.dfn]: HTMLElement, // HTMLDfnElement,
    [TagName.dialog]: HTMLDialogElement,
    [TagName.div]: HTMLDivElement,
    [TagName.dl]: HTMLDListElement,
    [TagName.dt]: HTMLElement, // HTMLDtElement,
    [TagName.em]: HTMLElement, // HTMLEmElement,
    [TagName.embed]: HTMLEmbedElement,
    [TagName.fieldset]: HTMLFieldSetElement,
    [TagName.figcaption]: HTMLElement, // HTMLFigcaptionElement,
    [TagName.figure]: HTMLElement, // HTMLFigureElement,
    [TagName.footer]: HTMLElement, // HTMLFooterElement,
    [TagName.form]: HTMLFormElement,
    [TagName.h1]: HTMLHeadingElement, // HTMLH1Element,
    [TagName.h2]: HTMLHeadingElement, // HTMLH2Element,
    [TagName.h3]: HTMLHeadingElement, // HTMLH3Element,
    [TagName.h4]: HTMLHeadingElement, // HTMLH4Element,
    [TagName.h5]: HTMLHeadingElement, // HTMLH5Element,
    [TagName.h6]: HTMLHeadingElement, // HTMLH6Element,
    [TagName.head]: HTMLHeadElement,
    [TagName.header]: HTMLElement, // HTMLHeaderElement,
    [TagName.hr]: HTMLHRElement,
    [TagName.html]: HTMLHtmlElement,
    [TagName.i]: HTMLElement, // HTMLIElement,
    [TagName.iframe]: HTMLIFrameElement,
    [TagName.img]: HTMLImageElement,
    [TagName.input]: HTMLInputElement,
    [TagName.ins]: HTMLModElement, // HTMLInsElement,
    [TagName.kbd]: HTMLElement, // HTMLKbdElement,
    [TagName.keygen]: HTMLElement, // HTMLKeygenElement,
    [TagName.label]: HTMLLabelElement,
    [TagName.legend]: HTMLLegendElement,
    [TagName.li]: HTMLLIElement,
    [TagName.link]: HTMLLinkElement,
    [TagName.main]: HTMLElement, // HTMLMainElement,
    [TagName.map]: HTMLMapElement,
    [TagName.mark]: HTMLElement, // HTMLMarkElement,
    [TagName.menu]: HTMLMenuElement,
    [TagName.menuitem]: HTMLElement, // HTMLMenuItemElement,
    [TagName.meta]: HTMLMetaElement,
    [TagName.meter]: HTMLMeterElement,
    [TagName.nav]: HTMLElement, // HTMLNavElement,
    [TagName.noscript]: HTMLElement, // HTMLNoscriptElement,
    [TagName.object]: HTMLObjectElement,
    [TagName.ol]: HTMLOListElement,
    [TagName.optgroup]: HTMLOptGroupElement,
    [TagName.option]: HTMLOptionElement,
    [TagName.output]: HTMLOutputElement,
    [TagName.p]: HTMLParagraphElement,
    [TagName.param]: HTMLParamElement,
    [TagName.picture]: HTMLPictureElement,
    [TagName.pre]: HTMLPreElement,
    [TagName.progress]: HTMLProgressElement,
    [TagName.q]: HTMLQuoteElement,
    [TagName.rb]: HTMLElement, // HTMLRbElement,
    [TagName.rbc]: HTMLElement, // HTMLRbcElement,
    [TagName.rp]: HTMLElement, // HTMLRpElement,
    [TagName.rt]: HTMLElement, // HTMLRtElement,
    [TagName.rtc]: HTMLElement, // HTMLRtcElement,
    [TagName.ruby]: HTMLElement, // HTMLRubyElement,
    [TagName.s]: HTMLElement, // HTMLSElement,
    [TagName.samp]: HTMLElement, // HTMLSampElement,
    [TagName.script]: HTMLScriptElement,
    [TagName.section]: HTMLElement, // HTMLSectionElement,
    [TagName.select]: HTMLSelectElement,
    [TagName.slot]: HTMLSlotElement,
    [TagName.small]: HTMLElement, // HTMLSmallElement,
    [TagName.source]: HTMLSourceElement,
    [TagName.span]: HTMLSpanElement,
    [TagName.strong]: HTMLElement, // HTMLStrongElement,
    [TagName.style]: HTMLStyleElement,
    [TagName.sub]: HTMLElement, // HTMLSubElement,
    [TagName.summary]: HTMLElement, // HTMLSummaryElement,
    [TagName.sup]: HTMLElement, // HTMLSupElement,
    [TagName.table]: HTMLTableElement,
    [TagName.tbody]: HTMLTableSectionElement, // HTMLTbodyElement,
    [TagName.td]: HTMLTableDataCellElement,
    [TagName.template]: HTMLTemplateElement,
    [TagName.textarea]: HTMLTextAreaElement,
    [TagName.tfoot]: HTMLTableSectionElement, // HTMLTfootElement,
    [TagName.th]: HTMLTableHeaderCellElement,
    [TagName.thead]: HTMLTableSectionElement, // HTMLTheadElement,
    [TagName.time]: HTMLTimeElement,
    [TagName.title]: HTMLTitleElement,
    [TagName.tr]: HTMLTableRowElement,
    [TagName.track]: HTMLTrackElement,
    [TagName.u]: HTMLElement, // HTMLUElement,
    [TagName.ul]: HTMLUListElement,
    [TagName.var]: HTMLElement, // HTMLVarElement,
    [TagName.video]: HTMLVideoElement,
    [TagName.wbr]: HTMLElement, // HTMLWbrElement,
}

/** Type guard that returns true if the given `node` is an `Element`. */
export function isElementNode(node: Node | Element): node is Element {
    // Note that Node.ELEMENT_NODE is specified to alway be the constant '1'.
    // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
    return node.nodeType === 1;
}

/** Type guard that returns true if the given `node` is a `Text` node. */
export function isTextNode(node: Node): node is Text {
    // Note that Node.TEXT_NODE is specified to alway be the constant '3'.
    // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
    return node.nodeType === 3;
}

/** Type guard that returns true if the given `node` has the given `tagName`. */
export function hasTagName<K extends keyof TagMap>(node: Node | Element, tagName: K): node is TagMap[K] {
    return (node as Element).tagName === tagName;
}
